/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_LIGHT_H
#define M_LIGHT_H

#include "renderqueue.h"
#include "movableobject.h"
#include "color.h"
#include "vector3.h"
#include "movableobjectvisitor.h"

#include <string>

namespace Engine
{
	class LightFactory;

	/**
	*   Representation of a dynamic light source in the scene.
	*/
	class Light : public MovableObject
	{
		friend class LightFactory;
	public:
		 /// Defines the type of light
        enum LightTypes
        {
            /// Point light sources give off light equally in all directions, so require only position not direction
            LT_POINT,
            /// Directional lights simulate parallel light beams from a distant source, hence have direction but no position
            LT_DIRECTIONAL,
            /// Spotlights simulate a cone of light from a source so require position and direction, plus extra values for falloff
            LT_SPOTLIGHT
        };

        /**
		*   Destructor.
        */
        ~Light();

		/**
		*   Sets the type of light - see LightTypes for more info.
        */
        void SetType(LightTypes type);

        /**
		*   Returns the light type.
        */
        LightTypes GetType(void) const;

		/**
		*   Sets the color of the diffuse light given off by this source.
        */
        void SetDiffuseColor(const Color& color);

        /**
		*   Returns the color of the diffuse light given off by this light source
        */
        const Color& GetDiffuseColor(void) const;

		/**
		*   Sets the color of the specular light given off by this source.
        */
        void SetSpecularColour(const Color& color);

        /**
		*   Returns the color of specular light given off by this light source.
        */
        const Color& GetSpecularColour(void) const;


        /**
		*    Sets the attenuation parameters of the light source ie how it diminishes with distance.
        *    @param range The absolute upper range of the light in world units
        *    @param constant The constant factor in the attenuation formula: 1.0 means never attenuate, 0.0 is complete attenuation
        *    @param linear The linear factor in the attenuation formula: 1 means attenuate evenly over the distance
        *    @param quadratic The quadratic factor in the attenuation formula: adds a curvature to the attenuation formula.
        */
        void SetAttenuation(REAL range, REAL constant, REAL linear, REAL quadratic);

        /**
		*   Returns the absolute upper range of the light.
        */
        REAL GetAttenuationRange(void) const;

		/**
		*   Returns the constant factor in the attenuation formula.
        */
        REAL GetAttenuationConstant(void) const;

        /**
		*   Returns the linear factor in the attenuation formula.
        */
        REAL GetAttenuationLinear(void) const;

        /**
		*   Returns the quadric factor in the attenuation formula.
        */
        REAL GetAttenuationQuadric(void) const;

        /**
		*   Returns the position of the light.
        */
        const Vector3<REAL> GetPosition(void) const;

		/**
		*   Sets the direction in which a light points.
        */
        void SetDirection(const Vector3<REAL>& vec);

        /**
		*   Returns the light's direction.
        */
        const Vector3<REAL>& GetDirection(void) const;

		/**
		*   Sets the range of a spotlight, i.e. the angle of the inner and outer cones and the rate of falloff between them.
        *   @param innerAngle Angle covered by the bright inner cone
        *   @param outerAngle Angle covered by the outer cone
        *   @param falloff The rate of falloff between the inner and outer cones. 1.0 means a linear falloff, less means slower falloff, higher means faster falloff.
        */
        void SetSpotlightRange(REAL innerAngle, REAL outerAngle, REAL falloff = 1.0);

        /**
		*   Returns the angle covered by the spotlights inner cone.
        */
        REAL GetSpotlightInnerAngle(void) const;

        /**
		*   Returns the angle covered by the spotlights outer cone.
        */
        REAL GetSpotlightOuterAngle(void) const;

        /**
		*   Returns the falloff between the inner and outer cones of the spotlight.
        */
        REAL GetSpotlightFalloff(void) const;

		/**
		*   Retrieves the local axis-aligned bounding box for this object.
        */
        virtual const AABB& GetBoundingBox(void) const;

		/**
		*   Method for supporting visitor pattern (see scene manager).
		*/
		virtual void Accept(MovableObjectVisitor* visitor);

	private:

		/**
		*   Constructor.
        */
		Light(const std::string& name);

		/**
		*   Internal method by which the movable object must add Renderable subclass instances to the rendering queue.
        */
		virtual void UpdateRenderQueue(RenderQueue* queue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList);

	private:
		LightTypes m_lightType;
        Color m_diffuse;
        Color m_specular;

        Vector3<REAL> m_direction;

        REAL m_spotOuter;
        REAL m_spotInner;
        REAL m_spotFalloff;
        REAL m_range;
        REAL m_attenuationConst;
        REAL m_attenuationLinear;
        REAL m_attenuationQuad;

		/// Is the derived transform dirty?
        mutable bool m_derivedTransformDirty;

	};

	/******************************************************************************************************/

	/**
	*   Factory object for creating Light instances
	*/
	class LightFactory : public MovableObjectFactory
	{
	public:
		LightFactory():m_type("Light") {}
		~LightFactory() {}

		/**
		*   Returns the factory type.
		*/
		virtual const std::string& GetType() const { return m_type; }

		/**
		*   Creates a new object.
		*   @param name Name of the object to create
		*   @return	An object created by the factory. The type of the object depends on	the factory.
		*/
		virtual Light* CreateInstance( const std::string& name );
	protected:
		std::string m_type;
	};
}

#endif
